
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        // JS 对象之扩展、密封及冻结（级别逐渐升高）
        // 扩展
        // - Object.isExtensible( )
        // - Object.preventExtensions( ) 限制添加新属性
        //新对象默认是可扩展的无论何种方式创建的对象，这里使用的是字面量方式
        var obj1 = {a:1};
        console.log(Object.isExtensible(obj1)); //true

        //等价于 使用属性描述符
        obj1 = Object.create({},{
            "a":{
                value : 1,
                configurable : true,//可配置
                enumerable : true,//可枚举
                writable : true//可写
            }
        }); 
        console.log(Object.isExtensible(obj1)); //t

        //新创建的对象默认是可扩展的
        var obj2 = {};
        console.log(Object.isExtensible(obj2));
        obj2.a = 1;//添加成功
        console.log("obj2:",obj2); //{a: 1}

        //将其变为不可扩展对象
        Object.preventExtensions(obj2);
        console.log(Object.isExtensible(obj2)); //false

        //使用传统方式，为不可扩展对象添加属性
        obj2.b = 2; //静默失败,不抛出错误
        obj2["c"] = 3; //静默失败,不抛出错误
        console.log("obj2:",obj2); //{a: 1} 没有加入新的属性和属性值即扩展失败 
        // 可以改原有属性的值
        obj2.a = 2;
        console.log("obj2:",obj2); //{a: 2}
        Object.getOwnPropertyDescriptors(obj2);
        //{value: 2, writable: true, enumerable: true, configurable: true}
        // 可以配置属性特性
        Object.getOwnPropertyDescriptors(obj2);
        //可以删除属性 
        delete obj2.a;

        //Part 2 seal 密封
        // - Object.isSealed( )
        // - Object.seal( ) 在extend的限制基础上，增加限制可配置属性特性
        (function () {
            var obj = {             //声明一个对象
                prop:function(){},
                foo:"bar"
            };
            //可以添加新的属性,已有属性的值可以修改,可以删除
            obj.foo = "baz";
            obj.lumpy = "woof";
            delete obj.prop;
            obj; //{foo: 'baz', lumpy: 'woof'}

            var o = Object.seal(obj);//将 obj 密封,且返回原对象
            console.log(o === obj);//true
            console.log(Object.isSealed(obj)); //true
            Object.getOwnPropertyDescriptors(obj)

            //仍然可以修改密封对象上的属性的值,但不能修改configurable特性(即其他特性不可配置和不可删除属性)
            obj.foo = "quux";//修改成功

            //任何除修改属性值以外的操作都会失败  
            obj.quaxxor = "the friendly duck";//静默失败,属性没有成功添加，sealed对象也是不能扩展
            delete obj.foo;//静默失败,属性没有删除成功

            Object.defineProperty(obj,"ohai",{value :17});//添加属性失败，sealed之后也不可扩展
            Object.defineProperty(obj,"foo",{value : "eit"});//修改成功 只能修改已有属性的属性值
            console.log(obj.foo);//“eit”

            //密封后不可扩展即不可添加新的属性，且不可删除属性
            // 只可以对现有的属性进行修改。
            Object.defineProperty(obj,"foo",{enumerable: false});//报错
            delete obj.foo; //删除失败
            Object.getOwnPropertyDescriptors(obj);
            // foo: {value: 'quux', writable: true, enumerable: true, configurable: false}
            // lumpy: {value: 'woof', writable: true, enumerable: true, configurable: false}
        })();

        //Part 3 freeze 
        // 使用方法与seal类似，对于属性特性的限制更为严格，练习将上述案例改为冻结
        // - Object.isFrozen( )
        // - Object.freeze( ) 在seal的限制基础上，增加限制可写属性特性
        (function () {
            var obj = {             //声明一个对象
                prop:function(){},
                foo:"bar"
            };
            //可以添加新的属性,已有属性的值可以修改,可以删除
            obj.foo = "baz";
            obj.lumpy = "woof";
            delete obj.prop;
            obj; //{foo: 'baz', lumpy: 'woof'}

            var o = Object.freeze(obj);
            console.log(o === obj);
            console.log(Object.isFrozen(obj));

            obj.foo = "quux";//修改不成功
            //obj {foo: 'baz', lumpy: 'woof'}

            obj.quaxxor = "the friendly duck";//静默失败
            delete obj.foo;//静默失败,属性没有删除成功
            // obj {foo: 'baz', lumpy: 'woof'}

            // 以下会报错
            Object.defineProperty(obj,"ohai",{value :17});//添加属性失败
            Object.defineProperty(obj,"foo",{value : "eit"});//修改不成功
            console.log(obj.foo);//baz
        })();
    </script>
</body>

</html>